//
//  Extension.swift
//  HYOnline
//
//  Created by zhuxuhong on 2017/3/9.
//  Copyright © 2017年 zhuxuhong. All rights reserved.
//

import UIKit
import WebKit

extension UINib{
    public static func named(_ name: String) -> UINib{
        return UINib(nibName: name, bundle: Bundle.main)
    }
    
    public static func loaded(_ name: String) -> [Any]?{
        return Bundle.main.loadNibNamed(name, owner: nil, options: nil)
    }
}

extension UINavigationBar{
	public func transparent(){
		isTranslucent = true
		shadowImage = UIImage()
		backgroundColor = .clear
		setBackgroundImage(UIImage(), for: .default)
		barTintColor = .clear
	}
}

extension UITabBarController{
	public var selectedVC: UIViewController?{
		return viewControllers?[selectedIndex]
	}
}

extension UIViewController: UIPopoverPresentationControllerDelegate{
    
    @IBAction func dismiss(_ sender: Any){
        dismiss(animated: true, completion: nil)
    }
    
    @IBAction func navBack(_ sender: Any){
        navigationController?.popViewController(animated: true)
    }
    
	public func asyncPerform(_ action: @escaping (() -> Void), after seconds: Int = 1){
		DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.seconds(seconds), execute: action)
	}
	
	public var navigationBackButtonTitle: String{
		set{
			navigationItem.backBarButtonItem = UIBarButtonItem(title: navigationBackButtonTitle, style: .plain, target: nil, action: nil)
		}
		get{
			return navigationItem.backBarButtonItem?.title ?? ""
		}
	}
	
	public var navigationBarFrame: CGRect{
		guard let bar = navigationController?.navigationBar else {
			return .zero
		}
		return bar.frame
	}
	
    public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
        return .none
    }
    
    public func push(_ vc: UIViewController){
        navigationController?.pushViewController(vc, animated: true)
    }
    
	public func popToRootVC(){
		navigationController?.popToRootViewController(animated: true)
	}
	
	public func pop(animated: Bool = true){
        navigationController?.popViewController(animated: animated)
    }
    
    public func present(vc: UIViewController, animated: Bool = true, completion: (() -> Void)? = nil){
        present(vc, animated: animated, completion: completion)
    }
    
    public func dismiss(completion: (() -> Void)? = nil){
		dismiss(animated: true, completion: completion)
    }
    
    public func perform(segue: String, sender: Any?){
        performSegue(withIdentifier: segue, sender: sender)
    }
	
	public func setNavigationBar(barTintColor: UIColor){
		let bar = navigationController?.navigationBar
		bar?.isTranslucent = true
		bar?.shadowImage = UIImage()
		bar?.barTintColor = barTintColor
		bar?.backgroundColor = barTintColor
		bar?.setBackgroundImage(UIImage.image(with: barTintColor, size: bar!.frame.size), for: .default)
		UIApplication.shared.keyWindow?.backgroundColor = barTintColor
	}
    
	public func setNavigationBar(isHidden: Bool, animated: Bool = true){
        navigationController?.setNavigationBarHidden(isHidden, animated: animated)
    }
	
	public static func make<T: UIViewController>(from storyboard: UIStoryboard) -> T?{
		let name = String(describing: T.self)
		return storyboard.instantiateViewController(withIdentifier: name) as? T
	}
	
	func setNavBarBackgroundImage(_ image: UIImage?){
		navigationController?.navigationBar.setBackgroundImage(image, for: .default)
	}
}

extension Bundle{
	public func loadNib<T: UIResponder>(at index: Int = 0) -> T?{
		return loadNibNamed(String(describing: T.self), owner: nil, options: nil)?[index] as? T
	}
}

extension TimeInterval{
    public var date: Date{
        return Date(timeIntervalSince1970: self / 1000)
    }
}

extension Date{
	static var now: Date{
		return Date()
	}
	var isInToday: Bool{
		return Calendar.current.isDateInToday(self)
	}
	var isInYesterday: Bool{
		return Calendar.current.isDateInYesterday(self)
	}
	var isInThisYear: Bool{
		return components.year == Date.now.components.year
	}
	var components: DateComponents{
		return Calendar.current.dateComponents(in: TimeZone.current, from: self)
	}
	var shortText: String{
		var format = "yyyy-MM-dd HH:mm"
		if isInToday {
			format = "HH:mm"
		}
		else if isInYesterday {
			format = "昨天HH:mm"
		}
		else if isInThisYear {
			format = "MM-dd HH:mm"
		}
		return DateFormatter.with(format: format).string(from: self)
	}
}

extension DateFormatter{
	public enum Format: String{
		case yyyyMMdd_HHmmss = "yyyy-MM-dd HH:mm:ss"
		case yyyyMMdd_HHmm = "yyyy-MM-dd HH:mm"
		case yyyyMMdd = "yyyy-MM-dd"
	}
	
	static func with(format: String) -> DateFormatter{
		let df = DateFormatter()
		df.dateFormat = format
		return df
	}
}

extension Date{
	static func since1970(milliseconds: TimeInterval) -> Date {
		return .init(timeIntervalSince1970: milliseconds / 1000)
	}
	
	static func since1970(seconds: TimeInterval) -> Date {
		return .init(timeIntervalSince1970: seconds)
	}
	
    public func string(format: DateFormatter.Format) -> String{
        return DateFormatter.with(format: format.rawValue).string(from: self)
    }
    
    public func string(format: String) -> String{
        return DateFormatter.with(format: format).string(from: self)
    }
	
	public func after(minutes: TimeInterval) -> Date{
		return addingTimeInterval(minutes * 60)
	}
	
	public func after(days: TimeInterval) -> Date{
		return addingTimeInterval(days * 60 * 60 * 24)
	}
}

extension UIWindow{
    public static var key: UIWindow{
        var window = UIApplication.shared.keyWindow!
        for w in UIApplication.shared.windows{
            guard String(describing: w.classForCoder) != "UIRemoteKeyboardWindow" else{
                window = w
                break
            }
        }
        return window
    }
	
	public var topViewController: UIViewController? {
		var vc = topVC(at: UIApplication.shared.keyWindow?.rootViewController)
		while vc?.presentedViewController != nil {
			vc = topVC(at: vc?.presentedViewController)
		}
		return topVC(at: vc)
	}
	
	private func topVC(at vc: UIViewController?) -> UIViewController?{
		if let nav = vc as? UINavigationController {
			return nav.topViewController
		}
		if let tab = vc as? UITabBarController {
			return tab.selectedViewController
		}
		return vc
	}
}

extension WKWebView{
	public func jsHeightBy(id: String, completion: @escaping ((CGFloat) -> Void)){
		let js = "document.getElementById('\(id)').offsetHeight"
		evaluateJavaScript(js) { (result, error) in
			if let height = result as? CGFloat{
				completion(height)
			}
			else if let error = error{
				print("[WkWebView JS执行失败] : \(js) : \(error)")
			}
		}
	}
}

extension UIWebView{
    public func jsHeightBy(id: String) -> Float{
        let h = stringByEvaluatingJavaScript(from: "document.getElementById('\(id)').offsetHeight")?.float
        return h ?? 0
    }
}

extension Array{
	public var jsonString: String?{
		return JSONSerialization.stringWith(self)
	}
	
    public func forset(handler: ((_ e: Any) -> Void)){
        for elem in self {
            handler(elem)
        }
    }
}

extension Dictionary{
    public var jsonString: String?{
		return JSONSerialization.stringWith(self)
    }
}

extension JSONSerialization{
	public static func stringWith(_ object: Any) -> String?{
		guard 
			isValidJSONObject(object),
			let data = try? data(withJSONObject: object, options: .prettyPrinted),
			let json = String(data: data, encoding: .utf8) 
		else {
			print("JSONObject to String 失败")
			return nil
		}
		return json
	}
}

extension UITableView{
    public func reloadSectionsWithoutAnimation(_ sections: IndexSet){
        guard !visibleCells.isEmpty else{
            return
        }
        UIView.performWithoutAnimation {
            reloadSections(sections, with: .none)
        }
    }
    public func reloadRowsWithoutAnimation(_ rows: [IndexPath]){
        guard !visibleCells.isEmpty else{
            return
        }
        UIView.performWithoutAnimation {
            reloadRows(at: rows, with: .none)
        }
    }
    
	public func deselectRow(at indexPath: IndexPath){
		deselectRow(at: indexPath, animated: true)
	}
	
	public func reloadRows(_ rows: [IndexPath], animation: UITableViewRowAnimation = .none){
		reloadRows(at: rows, with: animation)
	}
	
	public func dequeueHeaderFooterFrom(nib nibName: String, reuseId: String, index: Int = 0) -> UITableViewHeaderFooterView?{
		var view = dequeueReusableHeaderFooterView(withIdentifier: reuseId)
		if view == nil {
			view = Bundle.main.loadNibNamed(nibName, owner: nil, options: nil)?[index] as? UITableViewHeaderFooterView
		}
		return view
	}
	
	public func dequeueCellFrom(nib nibName: String, reuseId: String, index: Int = 0) -> UITableViewCell?{
		var cell = dequeueReusableCell(withIdentifier: reuseId)
		if cell == nil {
			cell = Bundle.main.loadNibNamed(nibName, owner: nil, options: nil)?[index] as? UITableViewCell
		}
		return cell
	}
	
    public func removeSpareLines(){
        tableFooterView = UIView()
    }
    
    public func registerCell(nibName name: String){
        register(UINib(nibName: name, bundle: nil), 
                 forCellReuseIdentifier: name)
    }
	
	func registerCell<T: UITableViewCell>(nibWithClass name: T.Type){
		let name = String(describing: name)
		register(UINib(nibName: name, bundle: nil), forCellReuseIdentifier: name)
	}
	
	func registerHeaderFooter<T: UITableViewHeaderFooterView>(nibWithClass name: T.Type){
		let name = String(describing: name)
		register(UINib(nibName: name, bundle: nil), forHeaderFooterViewReuseIdentifier: name)
	}
	
	func registerHeaderFooter<T: UITableViewHeaderFooterView>(withClass name: T.Type){
		register(T.self, forHeaderFooterViewReuseIdentifier: String(describing: name))
	}
    
    func registerCell<T: UITableViewCell>(withClass name: T.Type){
        register(T.self, forCellReuseIdentifier: String.init(describing: name))
    }
	
    func dequeueCell<T: UITableViewCell>(withClass name: T.Type) -> T {
		let reuseId = String(describing: name)
        var cell = dequeueReusableCell(withIdentifier: reuseId)
		if cell == nil {
			cell = UITableViewCell(style: .default, reuseIdentifier: reuseId)
		}
		return cell as! T
    }
    
    func dequeueCell<T: UITableViewCell>(withClass name: T.Type, for indexPath: IndexPath) -> T? {
        return dequeueReusableCell(withIdentifier: String(describing: name), for: indexPath) as? T
    }
    
    func dequeueHeaderFooter<T: UITableViewHeaderFooterView>(withClass name: T.Type) -> T? {
        return dequeueReusableHeaderFooterView(withIdentifier: String(describing: name)) as? T
    }
}

extension UICollectionView{
	func registerCell<T: UICollectionViewCell>(nibWithClass name: T.Type){
		let name = String.init(describing: name)
		registerCell(nibName: name)
	}
	
    public func registerCell(nibName name: String){
        register(UINib(nibName: name, bundle: nil), forCellWithReuseIdentifier: name)
    }
    
    public func registerCell<T: UICollectionViewCell>(withClass name: T.Type){
        register(T.self, forCellWithReuseIdentifier: String(describing: name))
    }
    
    public func registerHeader(nibName name: String){
        register(UINib(nibName: name, bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: name)
    }
    
    public func registerHeader<T: UICollectionReusableView>(withClass name: T.Type){
        register(T.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: String(describing: name))
    }
    
    public func registerFooter(nibName name: String){
        register(UINib(nibName: name, bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: name)
    }
    
    public func registerFooter<T: UICollectionReusableView>(withClass name: T.Type){
        register(T.self, forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: String(describing: name))
    }
    
    func dequeueCell<T: UICollectionViewCell>(withClass name: T.Type, for indexPath: IndexPath) -> T? {
        return dequeueReusableCell(withReuseIdentifier: String.init(describing: name), for: indexPath) as? T
    }

    func dequeueHeader<T: UICollectionReusableView>(withClass name: T.Type, for indexPath: IndexPath) -> T? {
        return dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: String(describing: name), for: indexPath) as? T
    }
    
    func dequeueFooter<T: UICollectionReusableView>(withClass name: T.Type, for indexPath: IndexPath) -> T? {
        return dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionFooter, withReuseIdentifier: String(describing: name), for: indexPath) as? T
    }
}

extension CAShapeLayer{
    /// 画圆环 - 并带动画是否
    public static func makeOutlineCircle(
        lineColor: UIColor = .black,
        lineWidth: CGFloat = 1.0,
        center: CGPoint,
        radius: CGFloat,
        startAngle: CGFloat = 0.0,
        endAngle: CGFloat = .pi * 2,
        animated: Bool = false) -> CAShapeLayer{
        
        let layer = CAShapeLayer()
        let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: CGFloat(endAngle), clockwise: true)
        layer.path = path.cgPath
        layer.strokeColor = lineColor.cgColor
        layer.fillColor = UIColor.clear.cgColor
        layer.lineWidth = lineWidth
        layer.path = path.cgPath
        
        if animated {
            let anim = CABasicAnimation(keyPath: "strokeEnd")
            anim.duration = 1
            anim.fromValue = 0
            anim.toValue = 1
            anim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
            layer.add(anim, forKey: "animStroke")
        }
        return layer
    }
}

extension UITextField{
    
    public var clearButtonIcon: UIImage?{
        get{
            return nil
        }
        set{
            guard let image = newValue else {
                return
            }
            let button = value(forKey: "clearButton") as? UIButton
            button?.setImage(image, for: .normal)
        }
    }
    
    
    public var placeholderColor: UIColor?{
        get{
            return nil
        }
        set{
            guard let text = placeholder,
                let color = newValue else {
                return
            }
            let attr = NSAttributedString(string: text, attributes: [NSAttributedString.Key.foregroundColor: color])
            attributedPlaceholder = attr
        }
    }
}


extension UIView{
    // Shadow
    
    public var shadowColor: UIColor? {
        get {
            return layer.shadowColor?.uiColor
        }
        set {
            guard let color = newValue else {
                layer.shadowColor = nil
                return
            }
            layer.shadowColor = color.cgColor
        }
    }
    
    
    public var shadowOffset: CGSize? {
        get {
            return layer.shadowOffset
        }
        set {
            guard let size = newValue else {
                layer.shadowOffset = .zero
                return
            }
            layer.masksToBounds = false
            layer.shadowOffset = size
        }
    }
    
    
    public var shadowOpacity: Float? {
        get {
            return layer.shadowOpacity
        }
        set {
            guard let opacity = newValue else {
                layer.shadowOpacity = 0
                return
            }
            layer.shadowOpacity = opacity
        }
    }
    
    
    public var shadowRadius: CGFloat? {
        get {
            return layer.shadowRadius
        }
        set {
            guard let radius = newValue else {
                layer.shadowRadius = 0
                return
            }
            layer.shadowRadius = radius
        }
    }
    
}


extension UIView{
    
    public var borderColor: UIColor? {
        get {
            return layer.borderColor?.uiColor
        }
        set {
            guard let color = newValue else {
                layer.borderColor = nil
                return
            }
            layer.borderColor = color.cgColor
        }
    }
    
    
    public var borderWidth: CGFloat {
        get {
            return layer.borderWidth
        }
        set {
            layer.borderWidth = newValue
        }
    }
    
    
    public var cornerRadius: CGFloat {
        get {
            return layer.cornerRadius
        }
        set {
            layer.masksToBounds = true
            layer.cornerRadius = abs(CGFloat(Int(newValue * 100)) / 100)
        }
    }
    
	public var size: CGSize{
		set{
			bounds.size = size
		}
		get{
			return bounds.size
		}
	}
	
    public var width: CGFloat{
		set{
			bounds.size.width = width
		}
		get{
			return bounds.size.width
		}
    }
    
    public var height: CGFloat{
		set{
			bounds.size.height = height
		}
		get{
			return bounds.size.height
		}
    }
    
    public var x: CGFloat{
		set{
			frame.origin.x = x
		}
		get{
			return frame.origin.x
		}
    }
    
    public var y: CGFloat{
		set{
			frame.origin.y = y
		}
		get{
			return frame.origin.y
		}
    }
    
    public func removeSubviews() {
        subviews.forEach({$0.removeFromSuperview()})
    }
    
    public var parentViewController: UIViewController? {
        weak var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder!.next
            if let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
    
    public var firstResponder: UIView? {
        guard !isFirstResponder else {
            return self
        }
        for subView in subviews {
            if subView.isFirstResponder {
                return subView
            }
        }
        return nil
    }
    
    // 清除xib上布局测试时的颜色设置
    public func deepClearBackgroundColor(excepts: [UIView]?){
        backgroundColor = UIColor.clear
        
        subviews.forEach({sub in
            if let ex = excepts, ex.contains(sub) {
                return
            }
            sub.deepClearBackgroundColor(excepts: excepts)
        })
    }
    
    public func cornerMaskLayerWith(corners: UIRectCorner = [], radius: CGFloat){
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let maskLayer = CAShapeLayer()
        maskLayer.path = path.cgPath
        maskLayer.frame = bounds
        layer.mask = maskLayer
    }
    
    public func border(color: UIColor, width: CGFloat, cornerRadius: CGFloat = 0)
    {
        layer.borderColor = color.cgColor
        layer.borderWidth = width
        layer.cornerRadius = cornerRadius
    }
    
    public func corner(radius: CGFloat){
		layer.masksToBounds = true
        layer.cornerRadius = radius
    }
    
    public func shadow(color: UIColor, offsetX: CGFloat, offsetY: CGFloat, radius: CGFloat, opacity: Float)
    {
        clipsToBounds = false
        layer.shadowColor = color.cgColor
        layer.shadowOffset = CGSize(width: offsetX, height: offsetY)
        layer.shadowRadius = radius
        layer.shadowOpacity = opacity //默认为0
    }
    
    public func renderAsImage(opaque: Bool = false) -> UIImage?{
        UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, UIScreen.main.scale)
        guard let context = UIGraphicsGetCurrentContext() else {
            return nil
        }
        layer.render(in: context)
        let image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image
    }
}

extension IndexSet{
    public static let zero = IndexSet(integer: 0)
    
    public static func with(_ integer: Int) -> IndexSet{
        return .init(integer: integer)
    }
}

extension IndexPath{
	public static let zero = IndexPath(row: 0, section: 0)
}

extension UserDefaults{
    // write
    public static func update(value: Any?, key: String){
        UserDefaults.standard.setValue(value, forKey: key)
        UserDefaults.standard.synchronize()
    }
    
    // read
    public static func intValue(key: String) -> Int?{
        return UserDefaults.standard.value(forKey: key) as? Int ?? 0
    }
    
    public static func boolValue(key: String) -> Bool?{
        return UserDefaults.standard.value(forKey: key) as? Bool
    }
    
    public static func stringValue(key: String) -> String?{
        return UserDefaults.standard.value(forKey: key) as? String ?? ""
    }
	
	enum Keys: String {
		case isAppFirstLaunch = "isAppFirstLaunchKey"
		case allowsLoginingAutomatically = "allowsLoginingAutomaticallyKey"
		case userName = "userNameKey"
		case password = "passwordKey"
		
		enum Student: String {
			case hasStyleTested = "hasStyleTestedKey"
		}
	}
}

extension UIDevice{
    public var isiOS10: Bool{
        return systemVersion.compare("10.0").rawValue >= 0
    }
    
    public var isiOS9: Bool{
        return systemVersion.compare("9.0").rawValue >= 0 && systemVersion.compare("10.0").rawValue < 0
    }
    
    public var isiOS8: Bool{
        return systemVersion.compare("8.0").rawValue >= 0 && systemVersion.compare("9.0").rawValue < 0
    }
    
    public var isIpad: Bool{
        return userInterfaceIdiom == .pad
    }
	
	public var isIphoneX: Bool{
		return UIApplication.shared.statusBarFrame.height == 44
	}
}

extension UIColor{
	static let appGreen = UIColor(hex: 0x21b87d)
	static let appBlue = UIColor(hex: 0x5fb7ec)
	static let appPurple = UIColor(hex: 0x8281ed)
	static let appOrange = UIColor(hex: 0xf6a623)
	static let appRed = UIColor(hex: 0xf76e67)
	static let border = UIColor(hex: 0xe9e9e9)
	
    public static var random: UIColor {
        let r = Int(arc4random_uniform(255))
        let g = Int(arc4random_uniform(255))
        let b = Int(arc4random_uniform(255))
        return UIColor(r: r, g: g, b: b)
    }
    
    public static func color(r: Int, g: Int, b: Int, a: CGFloat = 1) -> UIColor{
        return UIColor(r: r, g: g, b: b, a: a)
    }
    
    public static func color(hex: Int, alpha: CGFloat = 1) -> UIColor{
        return UIColor(hex: hex, alpha: alpha)
    }
    
    public convenience init(r: Int, g: Int, b: Int, a: CGFloat = 1) {
        self.init(red: r.cgFloat / 255.0, green: g.cgFloat / 255.0, blue: b.cgFloat / 255.0, alpha: a)
    }
    
    public convenience init(hex: Int, alpha: CGFloat = 1){
        self.init(r: (hex >> 16) & 0xff, g: (hex >> 8) & 0xff, b: hex & 0xff, a: alpha)
    }
    
    public func alpha(_ a: CGFloat) -> UIColor{
        return withAlphaComponent(a)
    }
}

extension UIButton{
    public func setTitle(_ title: String){
        titleLabel?.text = title
        setTitle(title, for: .normal)
		sizeToFit()
    }
    
    public func styleOfEnabled(_ enabled: Bool){
        isUserInteractionEnabled = enabled
        setTitleColor(titleColor(for: .normal)?.withAlphaComponent(enabled ? 1.0 : 0.5), for: .normal)
        backgroundColor = backgroundColor?.withAlphaComponent(enabled ? 1.0 : 0.3)
        
        let borderColor = layer.borderColor ?? UIColor.clear.cgColor
        layer.borderColor = UIColor(cgColor: borderColor).withAlphaComponent(enabled ? 1.0 : 0.5).cgColor
    }
}

extension UIScrollView{
    public func scrollTo(page: Int, duration: TimeInterval = 0, vertically isVertical: Bool, completion: ((Bool) -> Void)? = nil)
    {
        let value = isVertical ? page.cgFloat * bounds.height : page.cgFloat * bounds.width
        UIView.animate(withDuration: duration, delay: 0, options: .curveEaseInOut, animations: { 
            self.contentOffset = isVertical ? CGPoint(x: 0, y: value) : CGPoint(x: value, y: 0)
        }, completion: completion)
    }
    
    public func pageIndex(vertically isVertical: Bool) -> Int {
        return isVertical ? Int(contentOffset.y/height + 0.5) : Int(contentOffset.x/width + 0.5)
    }
    
    public func scrollToBottom(animated: Bool = true) {
        let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height)
        setContentOffset(bottomOffset, animated: animated)
    }
    
    public func scrollToTop(animated: Bool = true) {
        setContentOffset(CGPoint.zero, animated: animated)
    }
	
	var isLeftHit: Bool{
		return contentOffset.x == 0
	}
	
	var isRightHit: Bool{
		let maxOffset = contentSize.width - bounds.width
		return contentOffset.x == maxOffset
	}
	
	var isBottomHit: Bool{
		let maxOffset = contentSize.height - bounds.height
		return contentOffset.y == maxOffset
	}
	
	var isTopHit: Bool{
		return contentOffset.y == 0
	}
}

extension UIApplication{
	public func openURL(url: String){
		guard let url = URL(string: url), canOpenURL(url) else {
			fatalError("Open URL Failed")
		}
		if #available(iOS 10, *) {
			open(url, options: [:], completionHandler: nil)
		}
		else{
			openURL(url)
		}
	}
	
    public var name: String{
        guard let dict = Bundle.main.infoDictionary,
            let name = dict["CFBundleName"] as? String else{
                return ""
        }
        return name
    }
    
    public var displayName: String{
        guard let dict = Bundle.main.infoDictionary,
            let name = dict["CFBundleDisplayName"] as? String else{
                return "未知应用"
        }
        return name
    }
    
    public var version: String{
        guard let dict = Bundle.main.infoDictionary,
            let version = dict["CFBundleShortVersionString"] as? String else{
            
            return "1.0"
        }
        return version
    }
    
    public var buildVersion: String{
        guard let dict = Bundle.main.infoDictionary,
            let version = dict["CFBundleVersion"] as? String else{
                
                return "1.0"
        }
        return version
    }
}

extension CGRect{
    public func insetsWithMargins(_ margins: UIEdgeInsets) -> CGRect{
        return CGRect(x: margins.left, y: margins.top, width: width-margins.left-margins.right, height: height-margins.top-margins.bottom)
    }
	
	public var center: CGPoint{
		return CGPoint(x: width / 2, y: height / 2)
	}
}

extension CGSize{
	public init(both: CGFloat){
		self.init(width: both, height: both)
	}
}

extension UIImage{
    public static func named(_ name: String) -> UIImage?{
        return UIImage(named: name)
	}
	
	public func compressed(quality: CGFloat = 0.5) -> UIImage? {
		guard let data = UIImageJPEGRepresentation(self, quality) else {
			return nil
		}
		return UIImage(data: data)
	}
	
	public var original: UIImage {
		return withRenderingMode(.alwaysOriginal)
	}
	
	public var template: UIImage {
		return withRenderingMode(.alwaysTemplate)
	}
    
    public func clip(insets: UIEdgeInsets) -> UIImage?{
        var rect = CGRect(x: insets.left, y: insets.top, width: size.width-insets.left-insets.right, height: size.height-insets.top-insets.bottom)
        
        rect.origin.x *= scale
        rect.origin.y *= scale
        rect.size.width *= scale;
        rect.size.height *= scale;
        
        guard let cgImage = self.cgImage?.cropping(to: rect) else {
            return nil
        }
        return UIImage(cgImage: cgImage)
    }
	
	public static func image(with color: UIColor, size: CGSize) -> UIImage? {
		UIGraphicsBeginImageContextWithOptions(size, false, 1)
		color.setFill()
		UIRectFill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
		guard let image = UIGraphicsGetImageFromCurrentImageContext() else {
			return nil
		}
		UIGraphicsEndImageContext()
		guard let aCgImage = image.cgImage else {
			return nil
		}
		return UIImage(cgImage: aCgImage)
	}
	
	public func tint(with color: UIColor, opaque: Bool = false) -> UIImage{
		UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
		color.setFill()
		guard let context = UIGraphicsGetCurrentContext() else {
			return self
		}
		
		context.translateBy(x: 0, y: size.height)
		context.scaleBy(x: 1.0, y: -1.0);
		context.setBlendMode(CGBlendMode.normal)
		
		let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
		guard let mask = self.cgImage else {
			return self
		}
		context.clip(to: rect, mask: mask)
		context.fill(rect)
		
		let newImage = UIGraphicsGetImageFromCurrentImageContext()!
		UIGraphicsEndImageContext()
		return newImage
	}
}

extension String{
	public var float: Float? {
		let formatter = NumberFormatter()
		return formatter.number(from: self) as? Float
	}	
	public var trimmed: String {
		return trimmingCharacters(in: .whitespacesAndNewlines)
	}
	
	public var int: Int? {
		return Int(self)
	}
	
	public var url: URL? {
		return URL(string: self)
	}
	
    public static func isEmpty(_ string: String?) -> Bool{
        return string == nil || string?.characters.count == 0
    }
	
	public var length: Int{
		return characters.count
	}
    
	
    // 特殊输入字符校验
    public enum SpecialInputType: String{
        // 邮箱
        case email = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
        // 手机号
        case phone = "^((13[0-9])|(15[^4,\\D])|(18[0,0-9]))\\d{8}$"
        // 身份证
        case ID = "^(\\d{14}|\\d{17})(\\d|[xX])$"
    }
    
    func isValidate(type: SpecialInputType) -> Bool {
        let test: NSPredicate = NSPredicate(format: "SELF MATCHES %@", type.rawValue)
        return test.evaluate(with: self)
    }
    
    public func boundingHeight(with width: CGFloat, attributes: [NSAttributedString.Key: Any]?) -> CGFloat{
        let h = (self as NSString).boundingRect(with: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: attributes, context: nil).height
        return h
    }
}

extension UIEdgeInsets{
    public static func make(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> UIEdgeInsets{
        return UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
    }
    
    public mutating func top(with: CGFloat) -> UIEdgeInsets{
        self.top = with
        return self
    }
    public mutating func bottom(with: CGFloat) -> UIEdgeInsets{
        self.bottom = with
        return self
    }
    public mutating func left(with: CGFloat) -> UIEdgeInsets{
        self.left = with
        return self
    }
    public mutating func right(with: CGFloat) -> UIEdgeInsets{
        self.right = with
        return self
    }
	public init(both: CGFloat){
		self.init(top: both, left: both, bottom: both, right: both)
	}
}

extension NSMutableAttributedString{
	public convenience init(attrStrings: [NSAttributedString]){
		self.init()
		self.append(attrStrings: attrStrings)
	}
	
	@discardableResult public func append(attrStrings: [NSAttributedString]) -> NSMutableAttributedString{
		for sub in attrStrings {
			self.append(sub)
		}
		return self
	}
}

extension NSAttributedString{
	public convenience init(string: String, 
							font: UIFont = Font.p14.system,
							color: UIColor = .black, 
							lineHeightMultiple: CGFloat = 1.5, 
							alignment: NSTextAlignment = .left){
		
		let paragraph = NSMutableParagraphStyle()
		paragraph.lineHeightMultiple = lineHeightMultiple
		paragraph.alignment = alignment
		
		self.init(string: string, attributes: [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: color, NSAttributedString.Key.paragraphStyle: paragraph])
	}
}

extension NSObject{
    // post notification
    public func postNotification(name: String, info: [AnyHashable : Any]?){
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: name), object: nil, userInfo: info)
    }
    
    // add observer
    public func observeNotification(observer: Any, action: Selector, name: String){
        NotificationCenter.default.addObserver(observer, selector: action, name: NSNotification.Name(rawValue: name), object: nil)
    }
    
    // remove observer
    public func removeNotiObserver(observer: Any){
        NotificationCenter.default.removeObserver(observer)
    }
    
    public func SwiftClassFromNameString(_ className: String) -> AnyClass?{
        let name = "\(UIApplication.shared.name).\(className)"
        return NSClassFromString(name)
    }
}

extension UIResponder{
    // hide key board
    public func hideKeyboard(){
        UIApplication.shared.keyWindow?.endEditing(true)
    }
}

extension UIStoryboard{
	static let common: UIStoryboard = .make(name: "Common")
	static let launch: UIStoryboard = .make(name: "Launch")
	static let main: UIStoryboard = .make(name: "Main")
	static let profile: UIStoryboard = .make(name: "Profile")
	
	public var initialViewController: UIViewController?{
		return self.instantiateInitialViewController()
	}
    
    public static func make(name: String) -> UIStoryboard{
        return UIStoryboard(name: name, bundle: Bundle.main)
    }
}

extension UIStoryboardSegue{
	enum Student {
		
	}
	enum Teacher {
		enum Learning: String {
			case detailNode = "toLearningNodeDetailPage"
			case detailLeaf = "toLearningLeafDetailPage"
		}
	}
}

extension Int{
    public var float: Float{
        return Float(self)
    }
    
    public var cgFloat: CGFloat{
        return CGFloat(self)
    }
	
	public var secondsToTimeString: String{
		var str = ""
		
		let hours = self / 3600
		str += "\(hours)时"
		
		let minutes = (self - hours * 3600) / 60
		str += "\(minutes)分"
		
		let seconds = self - hours * 60 * 60 - minutes * 60
		str += "\(seconds)秒"
		
		return str
	}
}

extension Float{
	public var cgFloat: CGFloat{
		return CGFloat(self)
	}
}

extension CGFloat{
    public var int: Int{
        return Int(self)
    }
    
    // 接近0但不为0的基本很小值
    public static var zero: CGFloat{
        return 0.000000000001
    }
}

extension CGColor{
    public var uiColor: UIColor{
        return UIColor(cgColor: self)
    }
}


extension Notification.Name{
	
}

extension UIImagePickerController{
	public enum InfoKey{
		static let editedImage = "UIImagePickerControllerEditedImage"
		static let originalImage = "UIImagePickerControllerOriginalImage"
		static let mediaType = "UIImagePickerControllerMediaType"
		static let cropRect = "UIImagePickerControllerCropRect"
		static let mediaURL = "UIImagePickerControllerMediaURL"
		static let refrenceURL = "UIImagePickerControllerReferenceURL"
		static let mediaMetadata = "UIImagePickerControllerMediaMetadata"
	}
}
